//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ComparePratt2PatternsByCATH.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fstream>
#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define MAX_SIZE 500000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace PatternsByCATH {
  
  bool AscendentString(String* text, void* ref) {

    return (strcmp(text->Out(), ((String*)ref)->Out()) < 0)?true:false;

  }
  
}
       
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComparePratt2PatternsByCATH::ComparePratt2PatternsByCATH () {

  this->filenameOut                    = new String ();
  this->listPratt2PatternsByCATH       = new TListE <Pratt2PatternsByCATH> ();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComparePratt2PatternsByCATH::ComparePratt2PatternsByCATH (const ComparePratt2PatternsByCATH& comparePratt2PatternsByCATH) {

  this->filenameOut                    = new String (comparePratt2PatternsByCATH.filenameOut);
  this->listPratt2PatternsByCATH       = new TListE <Pratt2PatternsByCATH> (comparePratt2PatternsByCATH.listPratt2PatternsByCATH);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComparePratt2PatternsByCATH::ComparePratt2PatternsByCATH (const ComparePratt2PatternsByCATH* comparePratt2PatternsByCATH) {

  this->filenameOut                    = new String (comparePratt2PatternsByCATH->filenameOut);
  this->listPratt2PatternsByCATH       = new TListE <Pratt2PatternsByCATH> (comparePratt2PatternsByCATH->listPratt2PatternsByCATH);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComparePratt2PatternsByCATH::~ComparePratt2PatternsByCATH () {

  if (this->filenameOut)              delete this->filenameOut;
  if (this->listPratt2PatternsByCATH) delete this->listPratt2PatternsByCATH;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ComparePratt2PatternsByCATH::FilenameOut (String* filenameOut) {

  if (filenameOut)
    *(this->filenameOut) = *filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ComparePratt2PatternsByCATH::ListPratt2PatternsByCATH (TListE <Pratt2PatternsByCATH>* listPratt2PatternsByCATH) {

  if (listPratt2PatternsByCATH)
    *(this->listPratt2PatternsByCATH) = *listPratt2PatternsByCATH;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//

String* ComparePratt2PatternsByCATH::FilenameOut (void) {

  return this->filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <Pratt2PatternsByCATH>* ComparePratt2PatternsByCATH::ListPratt2PatternsByCATH (void) {

  return this->listPratt2PatternsByCATH;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
float ComparePratt2PatternsByCATH::ComputeSimilarity (TListE <String>* listElemA,  TListE <String>* listElemB) {

  if (!listElemA || !listElemB)
    return -1;
  
  float similarity = 0;
  float number     = 0;
  int   higher     = 0;
  
  listElemA->QuickSortListByArray(PatternsByCATH::AscendentString, NULL);
  listElemB->QuickSortListByArray(PatternsByCATH::AscendentString, NULL);

  char** arrayA = new char* [listElemA->Length()]; 
  char** arrayB = new char* [listElemB->Length()]; 
  
  listElemA->SetInitial();
  for (long i = 0; i < listElemA->Length(); i++) {
    listElemA->SetNext();
    if (listElemA->GetCurrent()->Length() > higher)
      higher = listElemA->GetCurrent()->Length();
    arrayA[i] = new char [listElemA->GetCurrent()->Length() + 1];
    strcpy(arrayA[i], listElemA->GetCurrent()->Out());
  }
  
  listElemB->SetInitial();
  for (long i = 0; i < listElemB->Length(); i++) {
    listElemB->SetNext();
    if (listElemB->GetCurrent()->Length() > higher)
      higher = listElemB->GetCurrent()->Length();
    arrayB[i] = new char [listElemB->GetCurrent()->Length() + 1];
    strcpy(arrayB[i], listElemB->GetCurrent()->Out());
  }
  
  char prev [higher + 1];
  
  prev[0] = '\0';

  for (long i = 0; i < listElemA->Length(); i++) {
    
    if (strcmp(arrayA[i], prev) != 0) {
      
      float counterA = 0;
      float counterB = 0;
      
      number++;
      
      for (long j = i; j < listElemA->Length(); j++)
        if (!strcmp(arrayA[i], arrayA[j]))
          counterA++;
        
      for (long j = 0; j < listElemB->Length(); j++)
        if (!strcmp(arrayA[i], arrayB[j]))
          counterB++;
      
      similarity += abs(counterA - counterB)/(counterA + counterB);
      
    }
    
    strcpy(prev, arrayA[i]);
    
  }

  prev[0] = '\0';
  
  for (long i = 0; i < listElemB->Length(); i++) {
    
    if (strcmp(arrayB[i], prev) != 0) {
  
      float counterA = 0;
      float counterB = 0;
      
      for (long j = 0; j < listElemA->Length(); j++)
        if (!strcmp(arrayB[i], arrayA[j]))
          counterA++;

      if (!counterA) {
        
        number++;

        for (long j = i; j < listElemB->Length(); j++)
          if (!strcmp(arrayB[i], arrayB[j]))
            counterB++;

        similarity += 1;

      }
      
    }

    strcpy(prev, arrayB[i]);
    
  }
  
  similarity = 1 - similarity/number;
  
  for (long i = 0; i < listElemA->Length(); i++) 
    if (arrayA[i]) delete arrayA[i];
  
  for (long i = 0; i < listElemB->Length(); i++) 
    if (arrayB[i]) delete arrayB[i];
 
  if (arrayA) delete arrayA;
  if (arrayB) delete arrayB;
  
  return similarity;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ComparePratt2PatternsByCATH::LoadData (void) {

  if (!this->listPratt2PatternsByCATH || !this->listPratt2PatternsByCATH->Length())
    return false;
  
  this->listPratt2PatternsByCATH->SetInitial();
  while (this->listPratt2PatternsByCATH->SetNext()) {
    Pratt2PatternsByCATH* currentPratt2PatternsByCATH = this->listPratt2PatternsByCATH->GetCurrent();
    if (!currentPratt2PatternsByCATH->Namefile() || !currentPratt2PatternsByCATH->Namefile()->Length() || !currentPratt2PatternsByCATH->LoadData())
      return false;
  }
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ComparePratt2PatternsByCATH::GetPatternsSimilarity (int positionInPatterns) {
  
  if (!this->listPratt2PatternsByCATH || !this->listPratt2PatternsByCATH->Length() || 
      !this->filenameOut              || !this->filenameOut->Length())
    return false;

  float                 matrixSimilarity [this->listPratt2PatternsByCATH->Length()] [this->listPratt2PatternsByCATH->Length()];
  float                 higherSimilarity = 0;
  
  TListE <String>*      listExtendedPatternA    = new TListE <String> ();
  TListE <String>*      listExtendedPatternB    = new TListE <String> ();
  
  SearchPrositePattern* newSearchPrositePattern = new SearchPrositePattern();
  
  Pratt2Patterns*       currentPratt2Patterns   = NULL; 
  long                  start;
  long                  end;

  for (long i = 0; i < this->listPratt2PatternsByCATH->Length(); i++) 
    for (long j = i; j < this->listPratt2PatternsByCATH->Length(); j++) {
      
      if (i == j) { 
        matrixSimilarity[i][j] = 1;
        continue;
      }

      listExtendedPatternA->Clear();
      listExtendedPatternB->Clear();

      start = (!positionInPatterns)?0:positionInPatterns;
      end   = (!positionInPatterns)?this->listPratt2PatternsByCATH->Get(i)->ListGroupPrattPatterns()->Length() - 1:positionInPatterns;

      for (long k = start; k <= end; k++) {
        
        currentPratt2Patterns = this->listPratt2PatternsByCATH->Get(i)->ListGroupPrattPatterns()->Get(k);

        currentPratt2Patterns->Patterns()->SetInitial();
        while (currentPratt2Patterns->Patterns()->SetNext()) {

          newSearchPrositePattern->Pattern(currentPratt2Patterns->Patterns()->GetCurrent()->Pattern());

          if (!newSearchPrositePattern->ExpandPattern()) {
            if (listExtendedPatternA) delete listExtendedPatternA;
            if (listExtendedPatternB) delete listExtendedPatternB;
            return false;
          }

          newSearchPrositePattern->ExtendedPattern()->SetInitial();
          while (newSearchPrositePattern->ExtendedPattern()->SetNext()) 
            listExtendedPatternA->Add(new String(newSearchPrositePattern->ExtendedPattern()->GetCurrent()));

        }

      }

      start = (!positionInPatterns)?0:positionInPatterns;
      end   = (!positionInPatterns)?this->listPratt2PatternsByCATH->Get(j)->ListGroupPrattPatterns()->Length() - 1:positionInPatterns;

      for (long k = start; k <= end; k++) {
        
        currentPratt2Patterns = this->listPratt2PatternsByCATH->Get(j)->ListGroupPrattPatterns()->Get(k);

        currentPratt2Patterns->Patterns()->SetInitial();
        while (currentPratt2Patterns->Patterns()->SetNext()) {

          newSearchPrositePattern->Pattern(currentPratt2Patterns->Patterns()->GetCurrent()->Pattern());

          if (!newSearchPrositePattern->ExpandPattern()) {
            if (listExtendedPatternA) delete listExtendedPatternA;
            if (listExtendedPatternB) delete listExtendedPatternB;
            return false;
          }

          newSearchPrositePattern->ExtendedPattern()->SetInitial();
          while (newSearchPrositePattern->ExtendedPattern()->SetNext()) 
            listExtendedPatternB->Add(new String(newSearchPrositePattern->ExtendedPattern()->GetCurrent()));

        }

      }

      matrixSimilarity[i][j] = matrixSimilarity[j][i] = ComputeSimilarity(listExtendedPatternA, listExtendedPatternB);
      
      if (matrixSimilarity[i][j] > higherSimilarity)
        higherSimilarity = matrixSimilarity[i][j];

    }
  
  ofstream file (this->filenameOut->Out());
  
  if (!file.is_open()) return false;
  
  for (long i = 0; i < this->listPratt2PatternsByCATH->Length(); i++) {
    for (long j = 0; j < this->listPratt2PatternsByCATH->Length(); j++) {
      file.width(10);
      file.precision(6);
      file.setf(ios::fixed, ios::floatfield);
      file << ((i != j)?(matrixSimilarity[i][j]/higherSimilarity):matrixSimilarity[i][j]);
    }
    file << endl;
  }
  
  file.close();
  
  if (listExtendedPatternA)     delete listExtendedPatternA;
  if (listExtendedPatternB)     delete listExtendedPatternB;
  if (newSearchPrositePattern)  delete newSearchPrositePattern;
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* ComparePratt2PatternsByCATH::GetExtendedPatterns (int positionInList,  int positionInPatterns) {

  TListE <String>*      listExtendedPattern     = new TListE <String> ();
  Pratt2Patterns*       currentPratt2Patterns   = this->listPratt2PatternsByCATH->Get(positionInList)->ListGroupPrattPatterns()->Get(positionInPatterns);
  SearchPrositePattern* newSearchPrositePattern = new SearchPrositePattern ();

  currentPratt2Patterns->Patterns()->SetInitial();
  while (currentPratt2Patterns->Patterns()->SetNext()) {

    newSearchPrositePattern->Pattern(currentPratt2Patterns->Patterns()->GetCurrent()->Pattern());

    if (!newSearchPrositePattern->ExpandPattern()) {
      if (listExtendedPattern) delete listExtendedPattern;
      return NULL;
    }

    newSearchPrositePattern->ExtendedPattern()->SetInitial();
    while (newSearchPrositePattern->ExtendedPattern()->SetNext()) 
      listExtendedPattern->Add(new String(newSearchPrositePattern->ExtendedPattern()->GetCurrent()));

  }
  
  if (newSearchPrositePattern)  delete newSearchPrositePattern;
  
  return listExtendedPattern;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComparePratt2PatternsByCATH ComparePratt2PatternsByCATH::operator= (const ComparePratt2PatternsByCATH& comparePratt2PatternsByCATH) {

  *(this->filenameOut)              = *(comparePratt2PatternsByCATH.filenameOut);
  *(this->listPratt2PatternsByCATH) = *(comparePratt2PatternsByCATH.listPratt2PatternsByCATH);

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//